package independence.hibernate;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;

/**
 * The base class for creating custom DAO implementations. Extend this class
 * to create custom DAO objects. This class contains CRUD operations for all
 * persistence operations you should require, but you may add methods in subclasses
 * to create more specific queries.
 * 
 * @param <T>	The type of the persistent object.
 * @param <ID>	The type of the persistent object's id field.
 * 
 * @author TK, TK Assembled for Independence "Honor."
 */
public abstract class GenericDAOImpl<T, ID extends Serializable> extends IndyDAOImpl 
	implements GenericDAO<T, ID> {
	/**
	 * The class type of the persistent value.
	 */
	protected Class<T> type;
	/**
	 * Default constructor for GenericDAOImpl.
	 * @param	type	The class object of the desired persistent object.
	 */
	public GenericDAOImpl(Class<T> type) {
		super();
		this.type = type;
	}
	/** {@inheritDoc} */
	@Override public void delete(T value) {
		this.delete(value, 100);
	}
	/** {@inheritDoc} */
	@Override public void delete(T value, Integer timeout) {
		Session session = this.getSession();
		Transaction trans = session.beginTransaction();
		trans.setTimeout(timeout);
		try {
			session.delete(value);
			trans.commit();
		} catch (HibernateException e) {
			trans.rollback();
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}
	}
	/** {@inheritDoc} */
	@Override public void deleteAll(Collection<T> collection) {
		this.deleteAll(collection, 100);
	}
	/** {@inheritDoc} */
	@Override public void deleteAll(Collection<T> collection, Integer timeout) {
		Session session = this.getSession();
		Transaction trans = session.beginTransaction();
		trans.setTimeout(timeout);
		try {
			for (T entity : collection) {
				session.delete(entity);
			}
			trans.commit();
		} catch (HibernateException e) {
			trans.rollback();
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}
	}
	/** {@inheritDoc} */
	@Override public List<T> getAll() {
		return this.getAll(100, 0, 0);
	}
	/** {@inheritDoc} */
	@Override public List<T> getAll(Integer timeout) {
		return this.getAll(timeout, 0, 0);
	}
	/** {@inheritDoc} */
	@SuppressWarnings(value = "unchecked")
	@Override public List<T> getAll(Integer timeout, Integer start, Integer limit) {
		Session session = this.getSession();
		Criteria criteria = session.createCriteria(type);
		criteria.setTimeout(timeout);
		if (!limit.equals(0)) criteria.setMaxResults(limit);
		criteria.setFirstResult(start);
		List<T> returner = null;
		try {
			returner = criteria.list();
		} catch (HibernateException e) {
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}
		
		return returner;
	}
	/** {@inheritDoc} */
	@Override public List<T> getAllByAttribute(String name, Object value) {
		return this.getAllByAttribute(name, value, 100, 0, 0);
	}
	/** {@inheritDoc} */
	@Override public List<T> getAllByAttribute(String name, Object value, Integer timeout) {
		return this.getAllByAttribute(name, value, timeout, 0, 0);
	}
	/** {@inheritDoc} */
	@SuppressWarnings(value = "unchecked")
	@Override public List<T> getAllByAttribute(String name, Object value,
			Integer timeout, Integer start, Integer limit) {
		Session session = this.getSession();
		Criteria criteria = session.createCriteria(type);
		criteria.setTimeout(timeout);
		criteria.setFirstResult(start);
		if (!limit.equals(0)) criteria.setMaxResults(limit);
		criteria.add(Restrictions.eq(name, value));
		List<T> returner = null;
		try {
			returner = criteria.list();
		} catch (HibernateException e)  {
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}
		
		return returner;
	}
	/** {@inheritDoc} */
	@Override public T getByAttribute(String name, Object value) {
		return getByAttribute(name, value, 100);
	}
	/** {@inheritDoc} */
	@SuppressWarnings(value = "unchecked")
	@Override public T getByAttribute(String name, Object value, Integer timeout) {
		Session session = this.getSession();
		Criteria criteria = session.createCriteria(type);
		criteria.setTimeout(timeout);
		criteria.add(Restrictions.eq(name, value));
		T returner = null;
		try {
			returner = (T)criteria.uniqueResult();
		} catch (HibernateException e) {
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}
		
		return returner;
	}
	/** {@inheritDoc} */
	@Override public T getById(ID value) {
		return getById(value, 100);
	}
	/** {@inheritDoc} */
	@SuppressWarnings(value = "unchecked")
	@Override public T getById(ID value, Integer timeout) {
		Session session = this.getSession();
		Transaction trans = session.beginTransaction();
		trans.setTimeout(timeout);
		T returner = null;
		try {
			returner = (T)session.get(type, value);
		} catch (HibernateException e) {
			e.printStackTrace();
			trans.rollback();
			throw e;
		} finally {
			session.close();
		}
		
		return returner;
	}
	/** {@inheritDoc} */
	@Override public void persist(T value) {
		this.persist(value, 100);
	}
	/** {@inheritDoc} */
	@Override public void persist(T value, Integer timeout) {
		Session session = this.getSession();
		Transaction trans = session.beginTransaction();
		trans.setTimeout(timeout);
		try {
			session.persist(value);
			trans.commit();
		} catch (HibernateException e) {
			trans.rollback();
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}
	}
	/** {@inheritDoc} */
	@Override public void persistAll(Collection<T> collection) {
		this.persistAll(collection, 100);
	}
	/** {@inheritDoc} */
	@Override public void persistAll(Collection<T> collection, Integer timeout) {
		Session session = this.getSession();
		Transaction trans = session.beginTransaction();
		trans.setTimeout(timeout);
		try {
			for (T entity : collection) {
				session.persist(entity);
			}
			trans.commit();
		} catch (HibernateException e) {
			trans.rollback();
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}
	}
	/** {@inheritDoc} */
	@Override public void persistOrUpdate(T value) {
		this.persistOrUpdate(value, 100);
	}
	/** {@inheritDoc} */
	@Override public void persistOrUpdate(T value, Integer timeout) {
		Session session = this.getSession();
		Transaction trans = session.beginTransaction();
		trans.setTimeout(timeout);
		try {
			session.saveOrUpdate(value);
			trans.commit();
		} catch (HibernateException e) {
			trans.rollback();
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}
	}
	/** {@inheritDoc} */
	@Override public void persistOrUpdateAll(Collection<T> collection) {
		this.persistOrUpdateAll(collection, 100);
	}
	/** {@inheritDoc} */
	@Override public void persistOrUpdateAll(Collection<T> collection, Integer timeout) {
		Session session = this.getSession();
		Transaction trans = session.beginTransaction();
		trans.setTimeout(timeout);
		try {
			for (T entity : collection) {
				session.saveOrUpdate(entity);
			}
			trans.commit();
		} catch (HibernateException e) {
			trans.rollback();
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}
	}
	/** {@inheritDoc} */
	@Override public void update(T value) {
		this.update(value, 100);
	}
	/** {@inheritDoc} */
	@Override public void update(T value, Integer timeout) {
		Session session = this.getSession();
		Transaction trans = session.beginTransaction();
		trans.setTimeout(timeout);
		try {
			session.update(value);
			trans.commit();
		} catch (HibernateException e) {
			trans.rollback();
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}
	}
	/** {@inheritDoc} */
	@Override public void updateAll(Collection<T> collection) {
		this.updateAll(collection, 100);
	}
	/** {@inheritDoc} */
	@Override public void updateAll(Collection<T> collection, Integer timeout) {
		Session session = this.getSession();
		Transaction trans = session.beginTransaction();
		trans.setTimeout(timeout);
		try {
			for (T entity : collection) {
				session.update(entity);
			}
			trans.commit();
		} catch (HibernateException e) {
			trans.rollback();
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}
	}
}
